#!/usr/bin/python

import os
import shutil
import tempfile
import unittest

import d2s
import util


TEST_DOCBOOK = """<?xml version="1.0"?>
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<preface id="Preface">
  <title>Preface</title>
  <para>On the Internet, popularity is swift and fleeting. A mention of your
  website on a popular blog can bring 300,000 potential customers your way at
  once, all expecting to find out who you are and what you have to offer. But
  if you're a small company just starting out, your hardware and software
  aren't likely to be able to handle that kind of traffic. Chances are, you've
  sensibly built your site to handle the 30,000 visits per hour you're
  actually expecting in your first 6 months. Under heavy load, such a system
  would be incapable of showing even your company logo to the 270,000 others
  that showed up to look around. And those potential customers are not likely
  to come back after the traffic has subsided.</para>

  <sect1 id="Acknowledgments_for_the_2nd_Edition">
    <title>Acknowledgments for the 2nd Edition</title>
    <para>Yay for everybody!</para>

    <sect2 id="Inner_Section_One">
      <title>Inner Section One Title</title>
      <para>Inner section one content.</para>
    </sect2>
    <sect2 id="Inner_Section_Two">
      <title>Inner Section Two Title</title>
      <para>Inner section two content.</para>

      <sect3 id="Inner_Section_Two_One">
        <title>Inner Section Two-One Title</title>
        <para>Inner section two-one content.</para>
      </sect3>
    </sect2>
  </sect1>
</preface>
"""


class TestCollapseWhitespace(unittest.TestCase):
    def testCollapseWhitespacePara(self):
        self.assertEqual(
            '<para>one two three</para>',
            d2s.collapse_whitespace(
            '<para>one\ntwo\nthree</para>'))

    def testCollapseWhitespaceProgramListing(self):
        data = '<programlisting>one\ntwo\nthree</programlisting>'
        self.assertEqual(data, d2s.collapse_whitespace(data))


class TestStripProductionMarkup(unittest.TestCase):
    def testStripProductionMarkup(self):
        self.assertEqual(
            'foo bar baz',
            d2s.strip_production_markup(
            'foo bar baz<indexterm id="I_indexterm15_d1e25419" '
            'significance="normal"><primary>Administration '
            'Console</primary><secondary>Versions panel</secondary>'
            '</indexterm>'))
        self.assertEqual(
            'foo bar baz',
            d2s.strip_production_markup(
            'foo bar baz<?dbfo-need height="2in"?>'))
        self.assertEqual(
            'foo bar baz',
            d2s.strip_production_markup(
            'foo <phrase role="keep-together">bar</phrase> baz'))


class TestUnpara(unittest.TestCase):
    def testZeroParas(self):
        result = d2s.unpara(
            '<para></para>')
        self.assertEqual(
            '',
            result)

    def testOnePara(self):
        result = d2s.unpara(
            '<para>some text</para>')
        self.assertEqual(
            'some text',
            result)

    def testTwoParas(self):
        result = d2s.unpara(
            '<para>some text</para>\n\n<para>some more text</para>')
        self.assertEqual(
            'some text\n\nsome more text',
            result)

    def testTwoParasNoNewlines(self):
        result = d2s.unpara(
            '<para>some text</para><para>some more text</para>')
        self.assertEqual(
            'some text\n\nsome more text',
            result)

    def testTwoParasOneNewline(self):
        result = d2s.unpara(
            '<para>some text</para>\n<para>some more text</para>')
        self.assertEqual(
            'some text\n\nsome more text',
            result)

    def testTwoParasOneProgramlistingInMiddle(self):
        result = d2s.unpara(
            '<para>some text</para>\n\n'
            '<programlisting>listing text</programlisting>\n\n'
            '<para>some more text</para>')
        self.assertEqual(
            'some text\n\n'
            '<programlisting>listing text</programlisting>\n\n'
            'some more text',
            result)

    def testTwoParasOneProgramlistingAtStart(self):
        result = d2s.unpara(
            '<programlisting>listing text</programlisting>\n\n'
            '<para>some text</para>\n\n'
            '<para>some more text</para>')
        self.assertEqual(
            '<programlisting>listing text</programlisting>\n\n'
            'some text\n\n'
            'some more text',
            result)

    def testTwoParasOneProgramlistingAtEnd(self):
        result = d2s.unpara(
            '<para>some text</para>\n\n'
            '<para>some more text</para>\n\n'
            '<programlisting>listing text</programlisting>')
        self.assertEqual(
            'some text\n\n'
            'some more text\n\n'
            '<programlisting>listing text</programlisting>',
            result)

    def testCollapseParaLines(self):
        result = d2s.unpara(
            '<para>some text\non multiple\nlines</para>\n\n'
            '<para>some more text</para>\n\n')
        self.assertEqual(
            'some text on multiple lines\n\n'
            'some more text',
            result)


class TestParseDocBook(unittest.TestCase):
    def testParseDocBookSection(self):
        result = d2s._parse_docbook_section(TEST_DOCBOOK, '(?:preface|chapter)', 0)
        self.assertEqual(1, len(result))
        self.assertEqual('Preface', result[0].section_id)
        self.assertEqual('Preface', result[0].title)
        self.assertNotIn('<para>On the Internet, popularity', result[0].text)
        self.assertIn('On the Internet, popularity', result[0].text)
        self.assertNotIn('Yay for everybody', result[0].text)
        self.assertEqual(1, len(result[0].inner_sections))
        self.assertEqual('Acknowledgments_for_the_2nd_Edition', result[0].inner_sections[0].section_id)
        self.assertEqual('Acknowledgments for the 2nd Edition', result[0].inner_sections[0].title)
        self.assertIn('Yay for everybody', result[0].inner_sections[0].text)
        self.assertEqual(2, len(result[0].inner_sections[0].inner_sections))
        self.assertEqual('Inner_Section_One', result[0].inner_sections[0].inner_sections[0].section_id)
        self.assertEqual(0, len(result[0].inner_sections[0].inner_sections[0].inner_sections))
        self.assertEqual('Inner_Section_Two', result[0].inner_sections[0].inner_sections[1].section_id)
        self.assertEqual(1, len(result[0].inner_sections[0].inner_sections[1].inner_sections))
        self.assertEqual('Inner_Section_Two_One', result[0].inner_sections[0].inner_sections[1].inner_sections[0].section_id)
        self.assertEqual(0, len(result[0].inner_sections[0].inner_sections[1].inner_sections[0].inner_sections))

    def testParseDocBookDoubleEncodingBug(self):
        data = """<?xml version="1.0"?>
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<preface id="Preface">
  <title>Preface</title>
  <para><filename moreinfo="none">web.xml</filename> is an XML file with a root element of
      <code>&lt;web-app&gt;</code>. To map URL patterns to servlets, you
      declare each servlet with a <code>&lt;servlet&gt;</code> element, then
      declare the mapping with a <code>&lt;servlet-mapping&gt;</code> element.
      The <code>&lt;url-pattern&gt;</code> of a servlet mapping can be a full
      URL path, or a URL path with a <code>*</code> at the beginning or end to
      represent a part of a path. In this case, the URL pattern
      <code>/*</code> matches all URLs.</para>
</preface>
"""

        doc = util.Document()
        d2s.parse_docbook(doc, data, '(?:preface|chapter)', 0)
        self.assertIn('<code>&lt;web-app&gt;</code>', doc.top_sections[0].text)
        result = d2s.make_opml_text(doc)
        self.assertIn('&lt;code&gt;&amp;lt;web-app&amp;gt;&lt;/code&gt;', result)
        # TODO: hmm, this doesn't catch a double-encoding issue I noticed in the Scrivener result...

    def testParseDocBook(self):
        doc = util.Document()
        d2s.parse_docbook(doc, TEST_DOCBOOK, '(?:preface|chapter)', 0)
        result = doc.top_sections
        self.assertEqual(1, len(result))
        self.assertEqual('Preface', result[0].section_id)
        self.assertEqual('Preface', result[0].title)


class TestMakeDocumentFromBookXmlFile(unittest.TestCase):
    def setUp(self):
        self.output_dir = tempfile.mkdtemp()

    def tearDown(self):
        shutil.rmtree(self.output_dir)
        
    def testMakeDocumentFromBookXmlFile(self):
        book_xml_text = """<?xml version="1.0"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">

<book id="I_book_d1e1">
  <title>Programming Google App Engine</title>
  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="bookinfo.xml"/>
  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="dedication.xml"/>
  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="ch00_Preface.xml"/>
  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="ch01_Introduction.xml"/>
  <index/>
  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="colo.xml"/>
</book>
"""
        dedication_xml_text = """<?xml version="1.0"?>
<!DOCTYPE dedication PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<dedication>
  <para>For Lisa</para>
</dedication>
"""
        ch00_xml_text = """<?xml version="1.0"?>
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<preface id="Preface">
  <title>Preface</title>
  <para>On the Internet, popularity is swift and fleeting. A mention of your
  website on a popular blog can bring 300,000 potential customers your way at
  once, all expecting to find out who you are and what you have to offer. But
  if you're a small company just starting out, your hardware and software
  aren't likely to be able to handle that kind of traffic. Chances are, you've
  sensibly built your site to handle the 30,000 visits per hour you're
  actually expecting in your first 6 months. Under heavy load, such a system
  would be incapable of showing even your company logo to the 270,000 others
  that showed up to look around. And those potential customers are not likely
  to come back after the traffic has subsided.</para>

  <sect1 id="Acknowledgments_for_the_2nd_Edition">
    <title>Acknowledgments for the 2nd Edition</title>
    <para>Yay for everybody!</para>
  </sect1>
</preface>
"""
        ch01_xml_text = """<?xml version="1.0"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<chapter id="Introduction">
  <title>Introduction</title>
  <para>An introduction.</para>
</chapter>
"""
        colo_xml_text = """<?xml version="1.0"?>
<!DOCTYPE colophon PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<colophon id="colophon">
  <title>Colophon</title>
  <para>The colophon is the most important part of the book!</para>
</colophon>
"""

        book_xml_filename = os.path.join(self.output_dir, 'book.xml')
        fh = open(book_xml_filename, 'w')
        fh.write(book_xml_text)
        fh.close()
        fh = open(os.path.join(self.output_dir, 'dedication.xml'), 'w')
        fh.write(dedication_xml_text)
        fh.close()
        fh = open(os.path.join(self.output_dir, 'ch00_Preface.xml'), 'w')
        fh.write(ch00_xml_text)
        fh.close()
        fh = open(os.path.join(self.output_dir, 'ch01_Introduction.xml'), 'w')
        fh.write(ch01_xml_text)
        fh.close()
        fh = open(os.path.join(self.output_dir, 'colo.xml'), 'w')
        fh.write(colo_xml_text)
        fh.close()

        doc = d2s.make_document_from_book_xml_file(
            book_xml_filename)
        self.assertEqual(2, len(doc.top_sections))
        self.assertEqual('Preface', doc.top_sections[0].title)
        self.assertEqual('Introduction', doc.top_sections[1].title)


class TestMakeOpmlFile(unittest.TestCase):
    def setUp(self):
        self.output_dir = tempfile.mkdtemp()

    def tearDown(self):
        shutil.rmtree(self.output_dir)

    def testMakeOpmlSectionText(self):
        sec1 = util.Section('sec1_id', 'sec1 title', 'sec1 text')
        sec2 = util.Section('sec2_id', 'sec2 title', 'sec2 text')
        sec3 = util.Section('sec3_id', 'sec3 title', 'sec3 text')
        sec4 = util.Section('sec4_id', 'sec4 title', 'sec4 text')
        sec1.add_section(sec2)
        sec1.add_section(sec3)
        sec2.add_section(sec4)
        result = d2s.make_opml_section_text(sec1)
        self.assertEqual("""<outline text="sec1 title" _note="sec1 text">
<outline text="sec2 title" _note="sec2 text">
<outline text="sec4 title" _note="sec4 text">
</outline>
</outline>
<outline text="sec3 title" _note="sec3 text">
</outline>
</outline>""", result)

    def testMakeOpmlText(self):
        sec1 = util.Section('sec1_id', 'sec1 title', 'sec1 text')
        sec2 = util.Section('sec2_id', 'sec2 title', 'sec2 text')
        sec3 = util.Section('sec3_id', 'sec3 title', 'sec3 text')
        doc = util.Document()
        doc.top_sections = [sec1, sec2, sec3]
        result = d2s.make_opml_text(doc)
        self.assertIn("""<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<opml version="1.0">
    <head>
        <title></title>
        <dateCreated>""", result)
        self.assertIn("""</dateCreated>
    </head>
    <body>
<outline text="sec1 title" _note="sec1 text">
</outline><outline text="sec2 title" _note="sec2 text">
</outline><outline text="sec3 title" _note="sec3 text">
</outline>
    </body>
</opml>
""", result)

    def testMakeOpmlFile(self):
        pass  # TODO  result = d2s.make_opml_file(opml_path, doc)


# TODO: class TestD2SMain(unittest.TestCase)


if __name__ == '__main__':
    unittest.main()
